
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;


using Swaf;

namespace Swaf.Container
{
	/// <summary>
	/// Summary description for ContainerBase.
	/// </summary>
	public delegate void ExpirationNotification(object contents, ref bool cancel);

	[Serializable]
	public abstract class ContainerBase : INameAddressSupport
	{
		protected IDictionary m_fields;
		[NonSerialized] protected static string s_defaultFactoryKey = "GeneralContainerNodes";
		[NonSerialized] private static IClassFactory s_nodeFactory;
		public event ExpirationNotification NodeExpiring;
		public void raiseNodeExpiringEvent(object content, ref bool cancel)
		{
			if (NodeExpiring != null)
			{
				NodeExpiring(content,ref cancel);
			}
		}
		public ContainerBase()
		{
			//m_fields = new Hashtable();
			//m_fields = new BTreeDictionary();
			m_fields = new ThreadSafeHashtable();
		}
		public ContainerBase(IDictionary baseStore, bool reuseDictionary)
		{
			m_fields = new ThreadSafeHashtable();
			if (reuseDictionary)
				m_fields = baseStore;
			else
			{
				foreach (DictionaryEntry entry in baseStore)
					put(entry.Key.ToString(), entry.Value);
			}
		}

		#region INameAddressSupport Members

		public void put(string nameAddress, object val)
		{
			put(nameAddress, val, null);
		}

		public virtual void put(string nameAddress, object val, object extraInfo)
		{
			bool continueToPut = true;
			string fldName = NameAddress.getFirstAddress(nameAddress);
			if(canUseField(fldName)) {
				if(extraInfo != null || m_fields[fldName] == null)
				{
					if(canChangeBizFieldType(fldName))
					{
						string nodeInfo = defaultNodeInfo();
						if(extraInfo != null)
							nodeInfo = extraInfo.ToString();
						object node = createNewElement(fldName, nodeInfo, val);
						if (node is ICatalog )
						{
							((ICatalog) node).catalogName=nameAddress;
						}
						if (node is IExpireNode)
						{
							((IExpireNode) node).container=this;
						}
						if(node == null)
							m_fields[fldName] = val;
						else
							m_fields[fldName] = node;
						if(NameAddress.isSingleAddress(nameAddress))
							continueToPut = false;
					}
				}
				if(continueToPut)
				{
					//INameAddressSupport nameNode = null;
					object node = m_fields[fldName];

					if(node is INameAddressSupport && !NameAddress.isSingleAddress(nameAddress))
					{
						INameAddressSupport nameNode = (INameAddressSupport)node;
						nameNode.put(NameAddress.getRemainingAddress(nameAddress), val, infoForField(fldName));
					}
					else
						m_fields[fldName] = val;
					/*
					if(node is INameAddressSupport)
						nameNode = (INameAddressSupport)node;
					if(nameNode != null && !NameAddress.isSingleAddress(nameAddress))
						nameNode.put(NameAddress.getRemainingAddress(nameAddress), val, infoForField(fldName));
					else
						m_fields[fldName] = val;
					*/
				}
			}
			else
				//Application.currentApp.Log.println("Inapropriate to try and access field " + nameAddress); //TODO Throw more accurate exception		
				throw new BaseException(String.Format("Inapropriate to try and access field '{0}'", nameAddress));
		}

		public virtual object get(string nameAddress, object oneArg)
		{
			return this.get(nameAddress, new object[]{oneArg});
		}

		public virtual object get(string nameAddress)
		{
			return this.get(nameAddress, new object[]{});
		}

		public virtual object get(string nameAddress, params object[] infoArgs)
		{
			if(nameAddress == null) return this;
			
			string fldName = NameAddress.getFirstAddress(nameAddress);
			if(canUseField(fldName)) {
				object genNode = m_fields[fldName];
				if(!(genNode is INameAddressSupport))
					return genNode;
				
				INameAddressSupport node = (INameAddressSupport)genNode;
				object extraInfo = infoForField(fldName);
				object[] a = null;
				if(extraInfo != null)
				{
					a = new object[infoArgs.Length +1];
					a[0] = infoForField(fldName);
					Array.Copy(infoArgs, 0, a, 1, infoArgs.Length);
				}
				else
					a = infoArgs;
				return node.get(NameAddress.getRemainingAddress(nameAddress), a);
			}
			else
				//Application.currentApp.Log.println("Inapropriate to try and access field " + nameAddress); //TODO Throw more accurate exception		
				throw new BaseException(String.Format("Inapropriate to try and access field '{0}'", nameAddress));

			return null;
		}

		public virtual bool isGetOnly {get{return false;}}

		public virtual XmlSerializationSupport SerializationSupport {get{return XmlSerializationSupport.None;}}

		public virtual string Xml{get{return null;}}

		public virtual string getXml(string options){return null;}

		public virtual bool 
			containsField(string nameAddress)
		{
			if(NameAddress.isSingleAddress(nameAddress))
				return m_fields.Contains(nameAddress);
			else {
				INameAddressSupport node = (INameAddressSupport)m_fields[NameAddress.getFirstAddress(nameAddress)];
				if(node != null)
					return node.containsField(NameAddress.getRemainingAddress(nameAddress));
			}
			return false;
		}

		#endregion

		protected virtual object createNewElement(string keyName, string pattern, object extraInfo) {
			if(nodeFactory == null || pattern == null)
				return extraInfo;
			else
				return nodeFactory.createFromPattern(pattern, extraInfo);
		}


		protected abstract bool canUseField(string name);
		protected abstract bool canChangeBizFieldType(string name);
		protected virtual object infoForField(String name) {return null;}
		protected virtual string defaultNodeInfo() {return null;}

		public override string ToString()
		{
			string s = Xml;
			if(s == null)
				return base.ToString();
			else
				return s;
		}



		protected static IClassFactory nodeFactory 
		{get{
			 if(s_nodeFactory == null)
			 {
				 s_nodeFactory = Application.currentApp.factories[s_defaultFactoryKey];
				 //TODO: Uncomment this to work again after drop for 1.2.2 is out. MJD (9/21/04)
				 //if(s_nodeFactory == null)
				//	 Application.currentApp.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Unable to access needed factory for Container elements: " + s_defaultFactoryKey);
			 }
			 return s_nodeFactory;
		 }}

		public static void resetClassFactoryReferences()
		{
			s_nodeFactory = null;
		}

		public virtual IEnumerator GetEnumerator()
		{
			return m_fields.GetEnumerator();
		}

	}
}
